package co.fitstart.coach.service.impl;

import java.util.Date;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo2do.core.BusinessException;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.persistence.annotation.Dynamic;
import com.demo2do.core.utils.DateFormatUtils;
import com.demo2do.core.utils.DateUtils;

import co.fitstart.coach.dao.CoachDao;
import co.fitstart.coach.dao.CourseDao;
import co.fitstart.coach.dao.RestCalendarDao;
import co.fitstart.coach.service.CoachRestCalendarService;
import co.fitstart.entity.coach.CoachRestCalendar;
import co.fitstart.entity.coach.CoachRestType;
import co.fitstart.entity.course.schedule.CourseSchedule;
import co.fitstart.entity.principal.Principal;

/**
 * 
 * @author mohanlan
 *
 */
@Service("coachRestCalendarService")
public class CoachRestCalendarServiceImpl implements CoachRestCalendarService {
    
    private static final Logger logger = LoggerFactory.getLogger(CoachRestCalendarServiceImpl.class);
    
    @Autowired
    private GenericDaoSupport genericDaoSupport;
    
    @Dynamic
    private CourseDao courseDao;
    
    @Dynamic
    private RestCalendarDao restCalendarDao;
    
    @Dynamic
    private CoachDao coachDao;
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoachRestCalendarService#getWeeklyCoachRestCalendar(co.fitstart.entity.principal.Principal, java.util.Date)
     */
    public List<CoachRestCalendar> getWeeklyCoachRestCalendar(Principal principal, Date startDate) {
        
        return courseDao.listWeeklyCoachRestCalendar(principal, startDate, DateUtils.addDays(startDate, 6));
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoachRestCalendarService#getWeeklyCoachRestCalendar(co.fitstart.entity.principal.Principal, java.util.Date, co.fitstart.entity.coach.CoachRestType)
     */
    public List<CoachRestCalendar> getWeeklyCoachRestCalendar(Principal principal, Date startDate, CoachRestType coachRestType) {
        return courseDao.listWeeklyRestCalendar(principal, startDate, DateUtils.addDays(startDate, 6), coachRestType);
    }
    
    
    public List<String> getDailyCoachRestCalendar(Principal principal, Date day, CoachRestType coachRestType) {
        return courseDao.listDailyCoachRestCalendar(principal, day, coachRestType);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoachRestCalendarService#arrange(co.fitstart.entity.principal.Principal, co.fitstart.entity.coach.CoachRestType, java.util.Date, java.util.List)
     */
    public void arrange(Principal principal, CoachRestType coachRestType, Date day, List<String> arrangeHours) {
        
        // save coach_rest_calendar
        if(!arrangeHours.isEmpty()) {
            
            // 1. validate arrange coach_rest_calendar
            this.validateArrange(principal, coachRestType, day, arrangeHours);
            
            // 2. do save
            for (String arrangeHour : arrangeHours) {
                logger.trace("#arrange - save coach_rest_calendar with coach[{}], type[{}], day[{}], hour[{}]", principal.getId(), coachRestType.getAlias(), DateFormatUtils.format(day), arrangeHour);
                CoachRestCalendar coachRestCalendar = new CoachRestCalendar(principal, coachRestType, day, arrangeHour, false);
                genericDaoSupport.save(coachRestCalendar);
            }
            
        }
        
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoachRestCalendarService#cancel(co.fitstart.entity.principal.Principal, co.fitstart.entity.coach.CoachRestType, java.util.Date, java.util.List)
     */
    public void cancel(Principal principal, CoachRestType coachRestType, Date day, List<String> disableHours) {
        
        // disable coach_rest_calendar
        if(!disableHours.isEmpty()) {
            
            // 3. validate disable coach_rest_calendar
            List<CoachRestCalendar> calendars = this.validateDisable(principal, coachRestType, day, disableHours);
            
            // 4. do disable coach_rest_calendar
            for (CoachRestCalendar calendar : calendars) {
                logger.trace("#arrange - save coach_rest_calendar with coach[{}], type[{}], day[{}], hour[{}]", principal.getId(), coachRestType.getAlias(), DateFormatUtils.format(day), calendar.getHour());
                genericDaoSupport.update(calendar.disable());
            }
            
        }
        
    }
    
    /**
     * 
     * @param principal
     * @param coachRestType
     * @param day
     * @param hours
     */
    private void validateArrange(Principal principal, CoachRestType coachRestType, Date day, List<String> hours) {
        
        // 1. validate coach's exiting course schedule
        List<CourseSchedule> courseSchedules = courseDao.loadCourseSchedule(principal, day, hours);
        if(!courseSchedules.isEmpty()) {
            logger.error("#validateArrange - conflict of exiting course schedule!");
            throw new BusinessException("该教练有课程");
        }
        
        // 2.1 validate rest type's coach_rest_calendar
        if (coachRestType.isRest()) {
            
            List<CoachRestCalendar> calendars = restCalendarDao.listCoachRestCalendar(principal, day, hours);
            
            if(!calendars.isEmpty()) {
                logger.error("#validateArrange - conflict of coach's rest type rest calendar!");
                throw new BusinessException("该教练已有休息时间");
            }
        
        // 2.2 validate experience type's coach_rest_calendar
        } else {
            
            List<CoachRestCalendar> calendars = restCalendarDao.listCoachRestCalendar(principal, day, hours, CoachRestType.DUTY);
            
            if(!calendars.isEmpty()) {
                logger.error("#validateArrange - conflict of coach's experience type rest calendar!");
                throw new BusinessException("该教练已有休息时间");
            }
        }
    }
    
    /**
     * 
     * @param principal
     * @param coachRestType
     * @param day
     * @param hours
     * @return
     */
    private List<CoachRestCalendar> validateDisable(Principal principal, CoachRestType coachRestType, Date day, List<String> hours) {
        List<CoachRestCalendar> coachRestCalendars = restCalendarDao.getCoachRestCalendar(principal, day, hours, coachRestType);
        if(coachRestCalendars.isEmpty() ||  coachRestCalendars.size() != hours.size()) { // no rest calendar, arrange means create a record
            
            throw new BusinessException("该休息时间不存在，请重新刷新页面");
            
        }
        return coachRestCalendars;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoachRestCalendarService#validateSum(int, int, int)
     */
    public void validateSum(int restLimit, int restSum, int arrangeSum) {
        restSum = restSum + arrangeSum;
        if(restSum > restLimit) {
            logger.error("#validateSum - out of limit");
            throw new BusinessException("所选休息时间超出");
        }
    }
    
    /* (non-Javadoc)
     * @see co.fitstart.coach.service.CoachRestCalendarService#copyLastWeek(co.fitstart.entity.principal.Principal, java.util.Date)
     */
    public void copyLastWeek(Principal principal, Date thisMonday) {
        
        Date lastMonday = DateUtils.addWeeks(thisMonday, -1);
        
        List<CoachRestCalendar> thisCoachRestCalendars = this.getWeeklyCoachRestCalendar(principal, thisMonday, CoachRestType.REST);
        
        // if thisCoachRestCalendars is not empty, throw new BusinessException
        if(CollectionUtils.isNotEmpty(thisCoachRestCalendars)) {
            throw new BusinessException("复制必须清空本周休息时间");
        }
        
        List<CoachRestCalendar> baseCoachRestCalendars = this.getWeeklyCoachRestCalendar(principal, lastMonday, CoachRestType.REST);
        
        for(CoachRestCalendar baseCoachRestCalendar : baseCoachRestCalendars) {
            
            Date baseRestDate = baseCoachRestCalendar.getRestDate();
            String baseRestHour = baseCoachRestCalendar.getRestHour();
            CoachRestType coachRestType = baseCoachRestCalendar.getCoachRestType();
            
            // check whether coach has some classes to run
            Date restDateThisWeek = DateUtils.addWeeks(baseRestDate, 1);
            List<CourseSchedule> courseSchedules = courseDao.listCourseSchedule(principal, restDateThisWeek, baseRestHour);
            
            if(courseSchedules.isEmpty()) {
                
                // repeat last week
                CoachRestCalendar coachRestCalendar = new CoachRestCalendar(principal, coachRestType, restDateThisWeek, baseRestHour, false);
                genericDaoSupport.save(coachRestCalendar);
            
            }
            
        }
        
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoachRestCalendarService#clearThisWeek(co.fitstart.entity.principal.Principal, java.util.Date)
     */
    public void clearThisWeek(Principal principal, Date thisMonday) {
        List<CoachRestCalendar> coachRestCalendars = this.getWeeklyCoachRestCalendar(principal, thisMonday, CoachRestType.REST);
        if(CollectionUtils.isNotEmpty(coachRestCalendars)) {
            for(CoachRestCalendar coachRestCalendar : coachRestCalendars) {
                genericDaoSupport.update(coachRestCalendar.disable());
            }
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.CoachRestCalendarService#weeklyCalendarSum(co.fitstart.entity.principal.Principal, java.util.Date, co.fitstart.entity.coach.CoachRestType)
     */
    public int sumWeeklyCalendar(Principal principal, Date day, CoachRestType coachRestType) {
        
        Date startDate = DateUtils.getFirstWeekDay(day);
        
        List<CoachRestCalendar> calendars = this.getWeeklyCoachRestCalendar(principal, startDate, coachRestType);
        
        return calendars.isEmpty() ? 0 : calendars.size();
    }

}
